Skip to content

Add Flashbots MEV bundle submission support#32

Merged
koko1123 merged 5 commits intomainfrom
koko/flashbots-bundle-submission
Mar 9, 2026
Merged

Add Flashbots MEV bundle submission support#32
koko1123 merged 5 commits intomainfrom
koko/flashbots-bundle-submission

Conversation

@koko1123
Copy link
Contributor

@koko1123 koko1123 commented Mar 9, 2026

Closes #13

Summary

  • Add flashbots.Relay client with Flashbots auth signing (X-Flashbots-Signature header using EIP-191)
  • Implement eth_sendBundle (Flashbots v1), mev_sendBundle (MEV-Share), eth_callBundle (simulation), and eth_cancelBundle
  • Add flashbots.Bundle convenience builder for collecting signed transactions
  • Full type definitions for all request/response formats including MEV-Share privacy and validity options

API

const eth = @import("eth");

// Create relay client with auth key
var relay = eth.flashbots.Relay.init(allocator, "https://relay.flashbots.net", auth_key);
defer relay.deinit();

// Submit bundle
const result = try relay.sendBundle(.{
    .transactions = bundle.transactions(),
    .block_number = current_block + 1,
});

// Simulate bundle
const sim = try relay.callBundle(.{
    .transactions = bundle.transactions(),
    .block_number = current_block + 1,
});

// MEV-Share bundle
const mev_result = try relay.mevSendBundle(.{
    .body = &.{ .{ .tx = .{ .data = signed_tx } } },
    .inclusion = .{ .block = current_block + 1 },
});

References

Test plan

  • Auth header format and signature recovery verified
  • JSON serialization matches Flashbots spec for all methods
  • Response parsing for bundle hash and simulation results
  • All unit tests pass (zig build test)

Summary by CodeRabbit

  • New Features
    • Flashbots relay client for MEV bundle workflows: construct, sign, send, simulate/call, and cancel bundles; bundle options for inclusion, validity, and privacy; authenticated relay interactions
    • Added JSON-RPC method names and root-level export for the flashbots client
  • Tests
    • Extensive tests for header formation, bundle building, parameter generation, MEV structures, parsing, and relay lifecycle
  • Chores
    • Manifest version bumped for the release

Implement Flashbots relay client with eth_sendBundle, mev_sendBundle,
eth_callBundle, and eth_cancelBundle. Includes EIP-191 auth signing
(X-Flashbots-Signature header) and Bundle convenience builder.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f34bd3d6-9a03-4920-aaf3-398a6ea7b1e5

📥 Commits

Reviewing files that changed from the base of the PR and between 60103ca and 546f225.

📒 Files selected for processing (1)
  • src/flashbots.zig

📝 Walkthrough

Walkthrough

Adds a new Zig Flashbots/MEV relay client with bundle builder, Relay type (sendBundle, callBundle, mevSendBundle, cancelBundle), Flashbots auth header signing, JSON-RPC parameter builders/parsers, public bundle/result types, tests, JSON-RPC method constants, and a root export.

Changes

Cohort / File(s) Summary
Flashbots module
src/flashbots.zig
New file implementing a Flashbots/MEV relay client: public types (SendBundleOpts, CallBundleOpts, MevSendBundleOpts, results, MevBundleBody, Bundle), Bundle builder API, Relay struct with init/deinit and RPC methods (sendBundle, callBundle, mevSendBundle, cancelBundle, authAddress), Flashbots auth header computation, JSON param builders, response parsers, FlashbotsError, and tests.
JSON-RPC methods
src/json_rpc.zig
Added JSON-RPC method name constants: eth_sendBundle, eth_callBundle, eth_cancelBundle, mev_sendBundle.
Root export
src/root.zig
Exported the new flashbots module from the root namespace and added it to test imports.
Build metadata
build.zig.zon
Bumped package manifest version from 0.2.3 to 0.3.0.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Relay
    participant Signer as Auth_Signer
    participant HTTP as HTTP_Client
    participant Server as Relay_Server

    Client->>Relay: sendBundle(opts)
    Relay->>Relay: buildSendBundleParams(opts)
    Relay->>Relay: serialize JSON body
    Relay->>Signer: computeAuthHeader(body)
    Signer->>Signer: keccak256(body) and secp256k1 sign
    Signer-->>Relay: X-Flashbots-Signature header
    Relay->>HTTP: POST JSON-RPC with header
    HTTP->>Server: transmit request
    Server-->>HTTP: response
    HTTP-->>Relay: response payload
    Relay->>Relay: checkRpcError & parse result
    Relay-->>Client: SendBundleResult / error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nibble bytes and stitch a sig so neat,

Bundles bound and packed in tidy rows,
Headers hummed, the relay keeps the beat,
I hop them off where builder sunlight grows,
A carrot-coded bundle — quick, complete.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Flashbots MEV bundle submission support' clearly and accurately summarizes the main change, which is introducing a comprehensive Flashbots MEV relay client with bundle submission capabilities.
Linked Issues check ✅ Passed The PR implements all core objectives from issue #13: sendBundle/mevSendBundle RPC methods, Flashbots auth signing with EIP-191, Bundle builder, callBundle simulation, cancelBundle, and complete type definitions for requests/responses including MEV-Share privacy/validity options.
Out of Scope Changes check ✅ Passed All changes are directly within scope: new flashbots.zig module implements MEV bundle submission, json_rpc.zig adds required method constants, root.zig exports the module, and build.zig.zon bumps version. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch koko/flashbots-bundle-submission

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link

vercel bot commented Mar 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
eth-zig Ready Ready Preview, Comment Mar 9, 2026 9:51pm

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/flashbots.zig (1)

108-133: Consider documenting the ownership model for transaction data.

The Bundle stores slices ([]const u8) to transaction data without copying. This means callers must ensure the transaction data remains valid for the lifetime of the Bundle. Consider adding a doc comment clarifying this, or alternatively provide a version that copies data if that's a common use case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/flashbots.zig` around lines 108 - 133, The Bundle struct currently stores
slices in txs without copying, so callers must keep backing memory alive; update
the code by adding a clear doc comment on Bundle (and on
addTransaction/transactions) that explains the ownership model and lifetime
requirement of []const u8 entries, and optionally implement a copying
alternative (e.g., addTransactionCopy that allocates via allocator, copies
signed_tx into owned storage and appends that slice) so users who expect
ownership can use it; reference symbols: Bundle, txs, addTransaction,
transactions and the allocator used for any copying routine.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/flashbots.zig`:
- Around line 564-576: The JSON parsing functions currently use
std.heap.page_allocator directly (e.g., checkRpcError), which breaks the
project's allocator threading and harms test/production memory tracking; change
these functions (checkRpcError, parseBundleHashResult, parseCallBundleResult) to
accept an allocator parameter (e.g., allocator: *std.mem.Allocator) and use that
allocator when calling std.json.parseFromSlice, then update all callers in Relay
to pass self.allocator so allocations are tracked consistently.

---

Nitpick comments:
In `@src/flashbots.zig`:
- Around line 108-133: The Bundle struct currently stores slices in txs without
copying, so callers must keep backing memory alive; update the code by adding a
clear doc comment on Bundle (and on addTransaction/transactions) that explains
the ownership model and lifetime requirement of []const u8 entries, and
optionally implement a copying alternative (e.g., addTransactionCopy that
allocates via allocator, copies signed_tx into owned storage and appends that
slice) so users who expect ownership can use it; reference symbols: Bundle, txs,
addTransaction, transactions and the allocator used for any copying routine.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ec2ce26a-e8c1-4130-b2f9-59a4a5de26fa

📥 Commits

Reviewing files that changed from the base of the PR and between 74174fe and 3f5bf0b.

📒 Files selected for processing (3)
  • src/flashbots.zig
  • src/json_rpc.zig
  • src/root.zig

…ship docs

Use caller's allocator instead of page_allocator in response parsing
functions for consistent memory tracking. Add doc comments clarifying
Bundle's borrowing semantics for transaction slices.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/flashbots.zig`:
- Around line 36-45: Change the CallBundleOpts.state_block_number from ?u64 to
an optional string type so callers can pass hex numbers or tags like "latest":
update the field signature in CallBundleOpts (currently state_block_number:
?u64) to state_block_number: ?[]const u8 (or to a small discriminated union type
e.g., BlockTag = union(enum){Number: u64, Tag: []const u8} and then
state_block_number: ?BlockTag) and then adjust any code that serializes or reads
state_block_number (serialization/JSON building for the Flashbots API) to accept
and forward either the string tag or a hex-encoded number appropriately. Ensure
default null behavior remains unchanged.
- Around line 631-634: The parsing function parseHexOrIntU256 only handles hex
strings and must accept base-10 decimal strings used by Flashbots for fields
like bundleGasPrice/coinbaseDiff/ethSentToCoinbase/gasFees; modify
parseHexOrIntU256 so its string branch checks if the string starts with "0x" and
parses as hex otherwise parses as a decimal integer (using the uint256 parsing
utility or equivalent), and ensure the callers (variables bundle_gas_price,
coinbase_diff, eth_sent, gas_fees in src/flashbots.zig) continue to use this
function; also update the test case to supply decimal-format strings (not
0x-prefixed hex) to match the documented Flashbots response.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7cc99c28-eedf-4548-b3d3-356268e3b5cf

📥 Commits

Reviewing files that changed from the base of the PR and between 3f5bf0b and 4f57098.

📒 Files selected for processing (1)
  • src/flashbots.zig

…ings

- Change state_block_number from ?u64 to ?BlockParam so callers can pass
  block tags like "latest" per the Flashbots spec
- Add decimal string parsing for bundleGasPrice/coinbaseDiff/etc fields
  which Flashbots returns as decimal strings, not hex
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/flashbots.zig`:
- Around line 389-392: The code is appending caller-provided strings
(replacement_uuid, privacy.hints, privacy.builders) directly into the JSON
buffer via buf.appendSlice/append which can break JSON or allow injection;
change the code that handles opts.replacement_uuid in the function that builds
the RPC body and the blocks that append privacy.hints and privacy.builders to
first JSON-escape the string (properly escape quotes, backslashes, and control
chars) and then append the escaped result instead of the raw input, reusing or
adding a helper like jsonEscape(allocator, src) and replace calls around
buf.appendSlice/append accordingly, and add a regression test that supplies
values with quotes/backslashes/control characters to verify the output is valid
JSON and fields are preserved.
- Around line 660-669: The parseDecimalU256 function can miss some overflows
because checking result < prev after result = result *% 10 +% digit is
insufficient; instead, before doing the multiply/add, check overflow by
comparing result against the u256 max thresholds: compute max_div_10 = u256.max
/ 10 and max_rem = u256.max % 10, then if result > max_div_10 or (result ==
max_div_10 and digit > max_rem) return error.InvalidResponse; only then perform
result = result *% 10 +% digit. This replaces the post-wrap check and uses the
u256 overflow bounds to prevent truncated/wrapped values in parseDecimalU256.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 36d9c4b1-b0ac-4805-aced-6ae6aad93920

📥 Commits

Reviewing files that changed from the base of the PR and between e5dacf7 and 60103ca.

📒 Files selected for processing (1)
  • src/flashbots.zig

- Add appendJsonEscaped helper for RFC 8259 string escaping (quotes,
  backslashes, control chars) and use it for replacement_uuid,
  privacy.hints, and privacy.builders
- Fix parseDecimalU256 to use pre-multiply overflow bounds check
  (maxInt(u256)/10) instead of post-wrap comparison
@koko1123 koko1123 merged commit 757ac11 into main Mar 9, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flashbots / MEV bundle submission

1 participant